package cn.slipi.admin.interceptor;

import cn.slipi.admin.common.annotation.NoLogin;
import cn.slipi.admin.common.annotation.RepeatCheck;
import cn.slipi.admin.common.exception.base.BizException;
import cn.slipi.admin.common.utils.StrUtils;
import cn.slipi.admin.common.utils.cache.redis.RedisClient;
import cn.slipi.admin.constants.AdminConstants;
import cn.slipi.admin.context.AdminThreadContext;
import cn.slipi.admin.context.AdminVisitor;
import cn.slipi.admin.context.dto.VisitorInfo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 认证验证
 * 判断接口是否使用NoLogin注解若使用则放行，判断用户是否登陆若登陆将用户信息设置进入上下文（ApiThreadContext）
 *
 * @author lee
 * @date 2019/1/18
 * @since 1.0.0
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {


    @Autowired
    private RedisClient redisClient;

    private static final Logger LOG = LoggerFactory.getLogger(AuthInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Class<?> clazz = handlerMethod.getBeanType();
        Method m = handlerMethod.getMethod();
        this.checkToken(clazz, m, request);
        this.checkRepeatToken(clazz, m, request);
        return true;
    }

    /**
     * 检查登录状态
     *
     * @param clazz
     * @param m
     * @param request
     */
    private void checkToken(Class clazz, Method m, HttpServletRequest request) {
        String token = request.getHeader(AdminConstants.TOKEN);
        if (StringUtils.isBlank(token)) {
            token = request.getParameter(AdminConstants.TOKEN_KEY);
        }
        // 接口是否需要登录
        if (isNoLogin(clazz, m)) {
            //不需要登陆判断是否存在用户，存在则放入上下文
            if (StrUtils.isNotBlank(token)) {
                VisitorInfo visitorInfo = this.redisClient.get(token, VisitorInfo.class);
                if (visitorInfo != null) {
                    putIntoContext(visitorInfo, token);
                }
            } else {
                putIntoContext(null, null);
            }
        } else {
            //需要登陆
            VisitorInfo visitorInfo = this.redisClient.get(token, VisitorInfo.class);
            if (null == visitorInfo) {
                putIntoContext(null, null);
                LOG.info(String.format("接口:[%s],需登陆,token:[%s]！", request.getRequestURI(), token));
                throw new BizException(401, "user.no.login");
            } else {
                putIntoContext(visitorInfo, token);
            }
        }
    }

    /**
     * 重复提交校验
     *
     * @param clazz
     * @param m
     * @param request
     */
    private void checkRepeatToken(Class clazz, Method m, HttpServletRequest request) {
        // 接口是否需要校验重复提交
        if (isCheckRepeat(clazz, m)) {
            String repeatToken = request.getHeader(AdminConstants.REPEAT_TOKEN);
            if (StrUtils.isNotBlank(repeatToken)) {
                //校验
                String repeatTokenKey = this.redisClient.get(repeatToken, String.class);
                if (StrUtils.isNotBlank(repeatTokenKey)) {
                    this.redisClient.deleteByKey(repeatTokenKey);
                } else {
                    throw new BizException(10000, "repeat.submit.error");
                }
            } else {
                throw new BizException(10000, "repeat.submit.error");
            }
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

    /**
     * 是否不需要登录
     *
     * @param clazz handler class
     * @param m     handler method
     * @return
     */
    private boolean isNoLogin(Class<?> clazz, Method m) {
        return clazz.isAnnotationPresent(NoLogin.class) || m.isAnnotationPresent(NoLogin.class);
    }

    /**
     * 是否需要检查重复提交
     *
     * @param clazz handler class
     * @param m     handler method
     * @return
     */
    private boolean isCheckRepeat(Class<?> clazz, Method m) {
        return clazz.isAnnotationPresent(RepeatCheck.class) || m.isAnnotationPresent(RepeatCheck.class);
    }

    private void putIntoContext(VisitorInfo visitorInfo, String token) {
        if (visitorInfo == null || StrUtils.isBlank(token)) {
            AdminThreadContext.putApiVisitor(null);
        }
        // 将用户信息放入上下文ThreadContext
        AdminThreadContext.putApiVisitor(
                new AdminVisitor(visitorInfo, token)
        );
    }
}
